/***************************************************************************************
 * Copyright 2020 Infineon Technologies AG ( www.infineon.com ).                       *
 * All rights reserved.                                                                *
 *                                                                                     *
 * Licensed  Material-Property of Infineon Technologies AG.                            *
 * This software is made available solely pursuant to the terms of Infineon            *
 * Technologies AG agreement which governs its use. This code and the information      *
 * contained in it are proprietary and confidential to Infineon Technologies AG.       *
 * No person is allowed to copy, reprint, reproduce or publish any part of this code,  *
 * nor disclose its contents to others, nor make any use of it, nor allow or assist    *
 * others to make any use of it - unless by prior Written express authorization of     *
 * Infineon Technologies AG and then only to the extent authorized.                    *
 *                                                                                     *
 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,            *
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,           *
 * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED.  IN NO       *
 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,     *
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,                 *
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;         *
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY             *
 * WHETHER IN  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR            *
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF              *
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.                                          *
 *                                                                                     *
 ***************************************************************************************/
/**
 * @file   nvm.c
 * @date   June, 2020
 * @brief  Implementation of NVM functions
 */
#include "nvm.h"

#include "helper.h"
#include "auths_api.h"
#include "auths_config.h"
#include "auths_status.h"
#include "crypto_lib.h"
#include "i2c_bus_command.h"

/** curve parameters for ECC authentication */
extern curve_parameter_t ECC_CURVE_163[1];

extern dwordvec_t gf2nReturnZ;
extern dwordvec_t gf2nCheck;
extern AuthS_Capability auths_capability;

static uint16_t IsNvmBusy(uint8_t *b_isBusy);
static uint16_t auths_write_nvm_bytes(uint16_t uw_Address, uint8_t *ubp_Data, uint16_t ub_BytesToProgram);

/**
* @brief check if NVM is busy
* @param bp_isBusy NVM busy status
*/
uint16_t IsNvmBusy(uint8_t *bp_isBusy)
{
	uint8_t ubData;
	uint16_t ret;

	if (bp_isBusy == NULL) {
		return APP_NVM_E_INPUT;
	}

	ret = Intf_ReadRegister(AUTHS_SFR_BUSY_STS, &ubData, 1);
	if (SDK_INTERFACE_SUCCESS != ret ) {
		return ret;
	}
	*bp_isBusy = (ubData >> BIT_NVM_BUSY) & 0x01;
	return TRUE;
}

/**
 * @brief NVM can only be read by page. Device attribute will determine the available user NVM pages.
 * @param nvm_start_page NVM start page number
 * @param ubp_data buffer to be return
 * @param page_count number of pages to be read
 * */
uint16_t auths_read_nvm(uint16_t nvm_start_page, uint8_t *ubp_data, uint8_t page_count){
	uint16_t ret;
	uint16_t read_len_byte = page_count*PAGE_SIZE_BYTE;
	uint8_t read_len=0;
	uint16_t remain_read_len = read_len_byte;

	//NVM location out of range
	if(nvm_start_page>AUTHS_LSC_4){
		return APP_NVM_E_INPUT;
	}

	//for User NVM page access, check against device attribute
	if(nvm_start_page<AUTHS_ODC1_ADDR){
		//Checks for valid user NVM page corresponding to device attributes
		if((nvm_start_page+page_count)>auths_capability.nvm_page_count){
			return APP_NVM_E_INPUT;
		}
	}

	//For User NVM operation, Host Auth must be performed. Except for 5K NVM type, last 16 bytes can be read without host auth
	if((nvm_start_page+page_count)<=auths_capability.nvm_page_count){
#if (HOST_AUTHENTICATION_FEATURE_SUPPORTED==1)		
		//For SLE95405/SLE95415, only the 1st 4kbit of User NVM will be under R/W access control.
		//Host is able to read the last 1.125kbit User NVM before Host Authentication is granted.
		if(nvm_start_page >= 128){
			for(uint8_t r=0; r<page_count; r++){
				ret = Intf_ReadRegister(nvm_start_page+r, ubp_data+(r*4), 4);
				if(SDK_INTERFACE_SUCCESS!=ret){	
					return ret;
				}
			}
			return APP_NVM_SUCCESS;
		}else{
			if(auths_capability.device_attribute.host_authentication_support==supported){
				if(auths_capability.host_auth_done==host_auth_not_done){
					return SDK_E_HOST_AUTH;
				}
			}
		}
#endif		
	}

	uint16_t i=0;

	//Read in NVM blocks of 256(SWI) or 255(I2C)
	while(remain_read_len){

		if(remain_read_len>=256){
			read_len=0x00;//
		}else{
			read_len=remain_read_len;
		}

		ret = Intf_ReadRegister(nvm_start_page+(i/4), ubp_data+i, read_len);
		if(SDK_INTERFACE_SUCCESS!=ret){	
			return ret;
		}

		if((read_len==0) && (auths_capability.active_interface==SWI)){//maximum read len of SWI is 256, represented by 0
			remain_read_len = remain_read_len-256;
			i+=256;
		}else if((read_len==0) && (auths_capability.active_interface==I2C)){ //maximum read len of I2C is 255, represented by 0 and 0xFF
			remain_read_len = remain_read_len-255;
			i+=255;
		}else{
			remain_read_len = remain_read_len-read_len;
			i+=read_len;
		}

	}
	return APP_NVM_SUCCESS;
}

uint16_t Cmd_Yfl_Get_Memory_Page(uint16_t addr, uint8_t *data)   	
{
	return auths_read_nvm(addr,data,8);
}

uint16_t IsNVMReady(void)
{
	uint8_t bp_isBusy=TRUE;
	uint16_t ret;
	// wait for NVM complete	
	uint8_t ulNvmTimeOut = 50;//100
	do{
		ret = IsNvmBusy(&bp_isBusy);
		if(ret != TRUE){
			return ret;
		}
		/* check for timeout */
		if( ulNvmTimeOut == 0u ){
			return APP_NVM_E_TIMEOUT;
		}
		ulNvmTimeOut--;
		delay_us(100);
	}while( bp_isBusy == 1);

	return APP_NVM_SUCCESS;

}

/**
* @brief Write data into user NVM page. The data to be written should be 4 bytes aligned. The NVM write programming delay is required.
* @param uw_Address Start NVM page.
* @param ubp_Data pointer to buffer holding values to program into NVM.
* @param page_count number of page to write
*/
uint16_t auths_write_nvm(uint16_t nvm_start_page, uint8_t *ubp_Data, uint8_t page_count){

	uint16_t ret;

	// check for valid user NVM page, checks for valid data length
	if((nvm_start_page+page_count > auths_capability.nvm_page_count)
	    && (nvm_start_page != AUTHS_LSC_BUF_1) && (nvm_start_page != AUTHS_LSC_BUF_2)
		&& (nvm_start_page != AUTHS_LSC_1)  && (nvm_start_page != AUTHS_LSC_2)
		&& (nvm_start_page != AUTHS_LSC_BUF_3) && (nvm_start_page != AUTHS_LSC_BUF_4)
		&& (nvm_start_page != AUTHS_LSC_3) && (nvm_start_page != AUTHS_LSC_4)){
		return APP_NVM_E_INPUT;
	}

	//Check if any of the user pages is locked
	if((nvm_start_page != AUTHS_LSC_BUF_1) && (nvm_start_page != AUTHS_LSC_BUF_2)
		&& (nvm_start_page != AUTHS_LSC_1)  && (nvm_start_page != AUTHS_LSC_2)
		&& (nvm_start_page != AUTHS_LSC_BUF_3) && (nvm_start_page != AUTHS_LSC_BUF_4)
		&& (nvm_start_page != AUTHS_LSC_3) && (nvm_start_page != AUTHS_LSC_4)){
		for(uint16_t i=0; i<page_count; i++){
			ret = auths_get_nvm_lock_status(nvm_start_page+i);
			if (APP_NVM_PAGE_LOCKED==ret ){
				return ret;
			}
		}
	}

	ret = auths_write_nvm_bytes(nvm_start_page, ubp_Data, page_count*4);
	return ret;

}

uint16_t Cmd_Yfl_Set_Memory_Page(uint16_t addr,uint8_t *data)   	
{
	return auths_write_nvm(addr,data,8);
}

uint16_t auths_write_nvm_bytes(uint16_t uw_Address, uint8_t *ubp_Data, uint16_t ub_BytesToProgram){

	uint16_t ret;
	//uint16_t page_count = ub_BytesToProgram/PAGE_SIZE_BYTE;

	if(ubp_Data == NULL){
		return APP_NVM_E_INPUT;
	}

	//NVM address must be 4 page aligned. Otherwise, 16 bytes register write will fail.
	//Example: 0, 4, 8, 12 and ...
	if(ub_BytesToProgram>=16){
		if((uw_Address%4) != 0){
			return APP_NVM_E_INPUT;
		}
	}

	//User NVM space write default to 16 bytes writing and remaining bytes will be done in 4 bytes blocks
	if(uw_Address< AUTHS_IFX_NVM_ADDR){
		uint16_t remaining_bytes = ub_BytesToProgram;
		uint8_t wr_len[1]={16};
		uint16_t i=0; //counter for 16 bytes write
		uint8_t j=0;  //counter for 4 bytes write

		// initialize the SWI write length to 16 bytes write
		if(auths_capability.active_interface==SWI){
			ret = Intf_WriteRegister(AUTHS_SFR_SWI_WD_LEN, wr_len, 1);
			if(SDK_INTERFACE_SUCCESS!=ret){ 
				return ret;
			}
		}
		// NVM write in blocks of 16 bytes
		while((remaining_bytes/16)>0){						
			ret = Intf_WriteRegister(uw_Address+(i/4), ubp_Data+i , 16);
			if(SDK_INTERFACE_SUCCESS!=ret){ 
				//if(ret==INF_I2C_E_WRITE){
				//	PRINT("error16: %d %d\r\n",uw_Address, remaining_bytes);
				//}
				return ret;
			}			
			delay_ms(NVM_PROGRAMMING_TIME);
			i+=16;
			remaining_bytes = remaining_bytes-16;
			
			if(APP_NVM_SUCCESS!= IsNVMReady()){
				return APP_NVM_E_TIMEOUT;
			}
		}
		
		// initialize the SWI write length to 4 bytes
		wr_len[0]=4;
		if(auths_capability.active_interface==SWI){
			ret = Intf_WriteRegister(AUTHS_SFR_SWI_WD_LEN, wr_len, 1);
			if(SDK_INTERFACE_SUCCESS!=ret){ 
				return ret;
			}
		}
		
		// NVM write in blocks of 4 bytes
		while((remaining_bytes/4)>0){			
			ret = Intf_WriteRegister(uw_Address+((i+j)/4), ubp_Data+i+j, 4);
			if(SDK_INTERFACE_SUCCESS!=ret){ 
				//if(ret==INF_I2C_E_WRITE){
				//	PRINT("error4: %d %d %d\r\n",uw_Address, remaining_bytes, j);
				//}
				return ret;
			}			
			delay_ms(NVM_PROGRAMMING_TIME);
			j+=4;
			remaining_bytes = remaining_bytes-4;

			if(APP_NVM_SUCCESS!= IsNVMReady()){
				return APP_NVM_E_TIMEOUT;
			}
		}
	}else{//Non-User NVM space write default to 4 bytes writing
		uint8_t wr_len[1]={4};
		uint8_t k=0;  //counter for 4 bytes write
		uint16_t remain_bytes = ub_BytesToProgram;

		// write the length to SWI WD length register
		if(auths_capability.active_interface==SWI){
			ret = Intf_WriteRegister(AUTHS_SFR_SWI_WD_LEN, wr_len, 1);
			if(SDK_INTERFACE_SUCCESS!=ret){ 
				return ret;
			}
		}
		

		// NVM write in blocks of 4 bytes
		while((remain_bytes/4)>0){			
			ret = Intf_WriteRegister(uw_Address+(k), ubp_Data+(k*4), 4 );	
			if(SDK_INTERFACE_SUCCESS!=ret){ 
				return ret;
			}			
			delay_ms(NVM_PROGRAMMING_TIME);

			remain_bytes= remain_bytes-4;
			k=k+4;
			if(APP_NVM_SUCCESS!= IsNVMReady()){
				return APP_NVM_E_TIMEOUT;
			}
		}
	}
	return APP_NVM_SUCCESS;
}

/**
* @brief Lock NVM page. NVM write programming delay is required.
* @param page NVM page need to be locked
*/
uint16_t auths_set_nvm_lock(uint8_t page){
	uint8_t byte_idx;
	uint8_t bit_idx;
	uint8_t data_wr;
	uint16_t ret;

	if(page >= AUTHS_USR_NVM_PAGE_SIZE){
		return APP_NVM_E_INPUT;
	}

	byte_idx = page >> 5;
	bit_idx = (page & 0x1f) >> 2;

	data_wr = 1 << bit_idx;

	ret = Intf_WriteRegister((AUTHS_SFR_USER_NVM_LOCK_1 + byte_idx), &data_wr, 1);
	if(SDK_INTERFACE_SUCCESS != ret){
		return ret;
	}
	delay_ms(NVM_PROGRAMMING_TIME);

	return APP_NVM_SUCCESS;
}

/**
* @brief Unlock all NVM pages that is locked
*/
uint16_t auths_unlock_nvm_locked()
{
		uint16_t ret;
		uint8_t data[UID_BYTE_LEN];
		mac_t mac_result;
		uint8_t mac_byte[MAC_BYTE_LEN];

		ret = Intf_ReadRegister(AUTHS_SFR_LOCK_STS, data, 1);
		if(AUTHS_SUCCESS != ret){
			return APP_NVM_E_READ_LOCK_STS;
		}

		if(((data[0] >> BIT_IFX_USERNVM_RESET_CONFIG ) & 0x01) == 0){
			return APP_NVM_DISABLE_UNLOCK;
		}

		//Note: This function must be run after auths_exe_ecc function
		if(auths_capability.ecc_done == ecc_not_done){
			return APP_ECC_E_ECC_NOT_DONE;
		}
		
		ret= auths_exe_ecc(0,0);
		if(APP_ECC_SUCCESS != ret){ 
			return ret;
		}
		
		// MAC password
		uint32_t Reset_Password[3];
		Get_Reset_Password(Reset_Password);
		
		//Get_Reset_Password(reset_password);
		do_mac(mac_result, Reset_Password, gf2nReturnZ, gf2nCheck, 0, ECC_CURVE_163, DEFAULT_MAC_GROUP);
		MacData2Byte(mac_result, mac_byte);

		if(auths_capability.active_interface==I2C){
			ret = i2c_bus_command_MACCR5(auths_capability.interface.i2c.device_address, mac_byte);
			if (INF_I2C_SUCCESS != ret) {
				return APP_NVM_E_MACCR5;
			}
		}
	
	    return APP_NVM_SUCCESS;

}

/**
* @brief read life span counter and decrease value. Change the decrease by value internally.
* Currently set to 1024.
* @param uw_lsc_DecVal pointer to uint16_t to store life span counter decrease value.
*/
uint16_t auths_get_lsc_decvalue( uint16_t *uw_lsc_DecVal )
{
	uint16_t ret;
	uint8_t rd_data;

	if (uw_lsc_DecVal == NULL) {
		return APP_NVM_E_INPUT;
	}

	ret = Intf_ReadRegister(AUTHS_SFR_LSC_DEC_VAL_L, &rd_data, 1);
	if (SDK_INTERFACE_SUCCESS != ret) {
		return ret;
	}
	*uw_lsc_DecVal = rd_data;

	ret = Intf_ReadRegister(AUTHS_SFR_LSC_DEC_VAL_H, &rd_data, 1);
	if (SDK_INTERFACE_SUCCESS != ret) {
		return ret;
	}
	*uw_lsc_DecVal |= rd_data << 8;

	if (*uw_lsc_DecVal == 0) {
		*uw_lsc_DecVal = 1024;
	}
	return APP_LSC_SUCCESS;
}

/**
* @brief Write life span counter and decrease value. NVM write programming delay is required.
* @param lsc_select Lifespan counter.
* @param uw_lsc_DecVal pointer to life span counter decrease value.
*/
uint16_t auths_set_lsc_decvalue( uint8_t lsc_select, uint16_t uw_lsc_DecVal)
{
	    uint32_t ulNvmTimeOut;
	    uint8_t ub_RdTimeOut = 50;
		uint8_t bp_isBusy;
		uint8_t ub_WrData;
		uint8_t ub_lscBusyBit;
		uint8_t wr_data,ubData;
		uint16_t ret;

		if(lsc_select == 0){
			ub_WrData = 1 << BIT_DEC_LSC_1;
			ub_lscBusyBit = BIT_LSC_DEC_1_STS;
		}else if(lsc_select == 1){
			ub_WrData = 1 << BIT_DEC_LSC_2;
			ub_lscBusyBit = BIT_LSC_DEC_2_STS;
		}else if(lsc_select == 2){
			ub_WrData = 1 << BIT_DEC_LSC_3;
			ub_lscBusyBit = BIT_LSC_DEC_3_STS;
		}else if(lsc_select == 3){
			ub_WrData = 1 << BIT_DEC_LSC_4;
			ub_lscBusyBit = BIT_LSC_DEC_4_STS;
		}else{
			return APP_NVM_E_INPUT;
		}

		wr_data = uw_lsc_DecVal & 0xff;
		ret = Intf_WriteRegister(AUTHS_SFR_LSC_DEC_VAL_L	, &wr_data, 1);
		if(SDK_INTERFACE_SUCCESS!=ret){ 
			return ret;
		}

		wr_data = uw_lsc_DecVal >> 8;
		ret = Intf_WriteRegister(AUTHS_SFR_LSC_DEC_VAL_H, &wr_data, 1);
		if(SDK_INTERFACE_SUCCESS!=ret){ 
			return ret;
		}

		/* wait for NVM state-machine to be ready for a new command */
		ulNvmTimeOut = 100;
		do
		{
			ret = Intf_ReadRegister( AUTHS_SFR_BUSY_STS, &ubData, 1);
			if(SDK_INTERFACE_SUCCESS!=ret) {
				return ret;
			}
			bp_isBusy = (ubData >> BIT_NVM_BUSY) & 0x01;
			/* check for timeout */
			if( ulNvmTimeOut == 0u ){
				return APP_NVM_E_TIMEOUT;
			}
			ulNvmTimeOut--;
		}while( bp_isBusy == 1);

		// set DEC_LSC_1/2 in LSC_KILL_CTRL
		ret = Intf_WriteRegister(AUTHS_SFR_LSC_KILL_CTRL, &ub_WrData, 1);
		if(SDK_INTERFACE_SUCCESS!=ret) {
			return ret;
		}

		// wait for decrease done
		delay_ms(NVM_PROGRAMMING_TIME);

			do{
				ret = Intf_ReadRegister(AUTHS_SFR_LSC_KILL_ACT_STS_2, &ubData,1);
				if(SDK_INTERFACE_SUCCESS!=ret){
					return ret;
				}
				if(ub_RdTimeOut == 0){
					return APP_LSC_E_TIMEOUT;
				}
				ub_RdTimeOut-- ;
			}while( ((ubData >> ub_lscBusyBit) & 0x01) != 0x00 );
		return APP_LSC_SUCCESS;
}

uint16_t Hal_Yfl_Write_Counter_Value(void)
{
	return auths_set_lsc_decvalue(3,1);
}
/**
* @brief Read life span counter value
* @param lsc_select select which life span counter to decrease
* @param lsc_value pointer to uint32_t to store current LifeSpanCounter into.
*/
uint16_t auths_get_lsc_value (uint8_t lsc_select, uint32_t *lsc_value)
{
	uint8_t ubData[ 4 ];
	uint16_t uwRegAddr;
	uint16_t ret = APP_NVM_INIT;

	if(lsc_select == 0){
		uwRegAddr = AUTHS_LSC_1;
	}else if(lsc_select == 1){
		uwRegAddr = AUTHS_LSC_2;
	}else if(lsc_select == 2){
		uwRegAddr = AUTHS_LSC_3;
	}else if(lsc_select == 3){
		uwRegAddr = AUTHS_LSC_4;
 	}else return APP_NVM_E_INPUT;

	if(lsc_value == NULL) {
		return APP_NVM_E_INPUT;
	}

	ret = Intf_ReadRegister(uwRegAddr, ubData, 4);
	if(SDK_INTERFACE_SUCCESS != ret){
		return ret;
	}else{
		*lsc_value =(ubData[3] << 24) + (ubData[2] << 16) + (ubData[1] << 8) + (ubData[0]) ;
	}

	/* all done well */
	return APP_LSC_SUCCESS;
}

uint16_t Hal_Yfl_Read_Counter_Value(uint32_t *dcvalue)
{
	return auths_get_lsc_value(3,dcvalue);
}
/**
* @brief Lock NVM page. NVM write programming delay is required.
* @param page NVM page need to be locked
*/
uint16_t auths_set_nvm_page_lock(uint8_t page){
	uint8_t byte_idx;
	uint8_t bit_idx;
	uint8_t data_wr;
	uint16_t ret;

	if(page >= AUTHS_USR_NVM_PAGE_SIZE){
		return APP_NVM_E_INPUT;
	}

	byte_idx = page >> 5;
	bit_idx = (page & 0x1f) >> 2;

	data_wr = 1 << bit_idx;

	ret = Intf_WriteRegister((AUTHS_SFR_USER_NVM_LOCK_1 + byte_idx), &data_wr, 1);
	if(SDK_INTERFACE_SUCCESS != ret){
		return ret;
	}
	delay_ms(NVM_PROGRAMMING_TIME);

	return APP_NVM_SUCCESS;
}

/**
* @brief Get NVM lock page status
* @param page NVM page check
*/
uint16_t auths_get_nvm_lock_status(uint8_t page){
	uint16_t uwRegAddr;
	uint16_t ret;
	uint8_t byte_idx;
	uint8_t bit_idx;
	uint8_t rd_data;

	if(page >= AUTHS_USR_NVM_PAGE_SIZE){
		return APP_NVM_E_INPUT;
	}

	byte_idx = page >> 5;
	bit_idx = (page & 0x1f) >> 2;

	uwRegAddr = AUTHS_SFR_USER_NVM_LOCK_STS_1 + byte_idx;
	ret = Intf_ReadRegister(uwRegAddr, &rd_data, 1);
	if(SDK_INTERFACE_SUCCESS != ret){
		return ret;
	}
	if(((rd_data >> bit_idx) & 0x01) == 0x01){
		return APP_NVM_PAGE_LOCKED;
	} else {
		return APP_NVM_PAGE_NOT_LOCKED;
	}
}

/**
* @brief Re-program life span counter with a new value. NVM write programming delay is required.
* @param lsc_select to select which life span counter to re-program
* @param lsc_value new life span counter value
*/
 uint16_t auths_set_lsc_value( uint8_t lsc_select, uint32_t lsc_value)
{
	uint8_t data_rd;
	uint8_t bit_ptr;
	uint16_t reg_addr;
	uint8_t data_wr[4];
	uint16_t ret;

	//Check for NVM write endurance
	//if(lsc_value > 100000) {
	//	return APP_NVM_E_LSC_ENDURANCE;
	//}

	// check status of LSC_EN_STS
	// if it is set, cannot re-program life span counter
	if(lsc_select == 0){
		bit_ptr = BIT_LSC_1_EN_STS;
	}
	else if(lsc_select == 1) {
		bit_ptr = BIT_LSC_2_EN_STS;
	}
	else if(lsc_select == 2){ 
		bit_ptr = BIT_LSC_3_EN_STS;
	}
	else if(lsc_select == 3){
		bit_ptr = BIT_LSC_4_EN_STS;
	}
	else{ 
		return APP_NVM_E_INPUT;
	}

	ret = Intf_ReadRegister(AUTHS_SFR_LSC_KILL_FEAT_STS_1, &data_rd, 1);
	if(SDK_INTERFACE_SUCCESS != ret){
		return ret;
	}
	if( ((data_rd >> bit_ptr) & 0x01) == 0x01) {
		return APP_LSC_EN_STATUS;
	}

	// program life span counter in NVM
	data_wr[0] = lsc_value & 0xff;
	data_wr[1] = (lsc_value >> 8) & 0xff;
	data_wr[2] = (lsc_value >> 16) & 0xff;
	data_wr[3] = (lsc_value >> 24) & 0xff;

	if(lsc_select == 0){
		reg_addr = AUTHS_LSC_1;
	}
	else if(lsc_select == 1){
		reg_addr = AUTHS_LSC_2;
	}
	else if(lsc_select == 2){
		reg_addr = AUTHS_LSC_3;
	}
	else if(lsc_select == 3){
		reg_addr = AUTHS_LSC_4;
	}
	else{
		return APP_NVM_E_INPUT;
	}

	ret = auths_write_nvm(reg_addr, data_wr, 4);
	if(APP_NVM_SUCCESS != ret) {
		return ret;
	}

	// program life span counter buffer in NVM
	if(lsc_select == 0){
		reg_addr = AUTHS_LSC_BUF_1;
	}
	else if(lsc_select == 1){
		reg_addr = AUTHS_LSC_BUF_2;
	}
	else if(lsc_select == 2){
		reg_addr = AUTHS_LSC_BUF_3;
	}
	else if(lsc_select == 3){
		reg_addr = AUTHS_LSC_BUF_4;
	}

	ret = auths_write_nvm(reg_addr, data_wr, 4);
	if(APP_NVM_SUCCESS != ret){ 
		return ret;
	}

	return APP_LSC_SUCCESS;
}

/**
* @brief Enables the auto-decrement function in the control register for life span counter 1. NVM write programming delay is requried.
*        Once enable, cannot clear it.
*/
uint16_t auths_set_lsc1_autodec(void){

 	uint8_t data_wr;
 	uint8_t rd_data;
    uint16_t ret = APP_NVM_INIT;

 	data_wr = 1 << BIT_LSC_1_AU_EN;

 	ret = Intf_WriteRegister(AUTHS_SFR_LSC_KILL_CONF_2, &data_wr, 1);
    if(SDK_INTERFACE_SUCCESS!=ret){
        printf("Error: Failed write register, ret=0x%X\r\n", ret);
    }

 	delay_ms(NVM_PROGRAMMING_TIME);

 	ret = Intf_ReadRegister(AUTHS_SFR_LSC_KILL_FEAT_STS_2, &rd_data, 1);
 	if(SDK_INTERFACE_SUCCESS!=ret){
		return ret;
	}

 	if(((rd_data >> BIT_LSC_1_AU_EN_STS) & 0x01) == 0x01){
 		 return APP_LSC_ENABLE_AUTODEC;
 	}
 	else{
        return APP_LSC_DISABLE_AUTODEC;
 	}
}

/**
* @brief enables life span counter protection, once enable, life span counter can't be re-programed. NVM write programming delay is required.
* @param lsc_number to select which life span counter to re-program
*/
uint16_t auths_set_lsc_protection(uint8_t lsc_number){
	uint8_t data_wr;
	uint16_t ret;

	if(lsc_number == 0){
		data_wr = 1 << BIT_LSC_1_EN;
	}
	else if(lsc_number == 1){ 
		data_wr = 1 << BIT_LSC_2_EN;
	}
	else if(lsc_number == 2){
		data_wr = 1 << BIT_LSC_3_EN;
	}
	else if(lsc_number == 3){ 
		data_wr = 1 << BIT_LSC_4_EN;
	}
	else{ 
		return APP_NVM_E_INPUT;
	}

	ret = Intf_WriteRegister( AUTHS_SFR_LSC_KILL_CONF_1, &data_wr, 1);
	if(SDK_INTERFACE_SUCCESS!=ret){
		return ret;
	}
	delay_ms(NVM_PROGRAMMING_TIME);
    return APP_LSC_SUCCESS ;
}

/**
* @brief  Set life span counter to 0. NVM write programming delay is required
* @param lsc_number to select which life span counter to re-program
*/
uint16_t auths_set_lsc_zero(uint8_t lsc_number){
	uint8_t data_wr;
	uint16_t ret;
	
	if(lsc_number == 0){
		data_wr = 1 << BIT_LSC_1_ZERO_EN;
	}
	else if(lsc_number == 1){
		data_wr = 1 << BIT_LSC_2_ZERO_EN;
	}
	else if(lsc_number == 2){
		data_wr = 1 << BIT_LSC_3_ZERO_EN;
	}
	else if(lsc_number == 3){
		data_wr = 1 << BIT_LSC_4_ZERO_EN;
	}
	else{ 
		return APP_NVM_E_INPUT;
	}

	ret = Intf_WriteRegister(AUTHS_SFR_LSC_KILL_CONF_1, &data_wr, 1);
	if(SDK_INTERFACE_SUCCESS!=ret) {
		return ret;
	}

	delay_ms(NVM_PROGRAMMING_TIME);

	if(lsc_number == 0) {
		data_wr = 1 << BIT_SET_LSC_1_ZERO;
	}
	else if(lsc_number == 1){
		data_wr = 1 << BIT_SET_LSC_2_ZERO;
	}
	else if(lsc_number == 2){
		data_wr = 1 << BIT_SET_LSC_3_ZERO;
	}
	else if(lsc_number == 3){
		  data_wr = 1 << BIT_SET_LSC_4_ZERO;
	}
	else{ 
		return APP_NVM_E_INPUT;
	}

	ret = Intf_WriteRegister(AUTHS_SFR_LSC_KILL_CTRL, &data_wr, 1);
	if(SDK_INTERFACE_SUCCESS != ret){
		return ret;
	}

	//delay_ms(NVM_PROGRAMMING_TIME);
    return APP_LSC_SUCCESS;
}

/**
* @brief Reset the life span counter protection. NVM write programming delay is required
* @param lsc_number to select which life span counter to reset
*/
uint16_t auths_reset_lsc(uint8_t lsc_number)
{
	uint16_t ret;
	uint8_t rd_data[UID_BYTE_LEN];
	mac_t mac_result;
	uint8_t mac_byte[MAC_BYTE_LEN];
	uint8_t bit_position;
	mac_t reset_password;

	//check if LSC reset feature is enabled
	ret = Intf_ReadRegister(AUTHS_SFR_LOCK_STS, rd_data, 1);
	if(SDK_INTERFACE_SUCCESS!= ret){
		return ret;
	}
	if(((rd_data[0] >> BIT_LSC_RESET_CONF ) & 0x01) == 0){
		return APP_LSC_DISABLE_RESET;
	}

	//LSC protection should be enabled first
	ret = Intf_ReadRegister(AUTHS_SFR_LSC_KILL_FEAT_STS_1, rd_data, 1);
	if(SDK_INTERFACE_SUCCESS!= ret){
		return ret;
	}

	if(lsc_number == 0){ 
		bit_position = BIT_LSC_1_EN_STS;
	}
	else if(lsc_number == 1) {
		bit_position = BIT_LSC_2_EN_STS;
	}
	else if(lsc_number == 2) {
		bit_position = BIT_LSC_3_EN_STS;
	}
	else if(lsc_number == 3){ 
		bit_position = BIT_LSC_4_EN_STS;
	}
    else{
		return APP_LSC_E_INPUT;
	}

	if(((rd_data[0] >> bit_position) & 0x01) != 0x01){
		return APP_LSC_NOT_LOCKED;
	}

	if(auths_capability.ecc_done == ecc_not_done){

		ret = auths_exe_ecc(0, MAC_GROUP_0);
		if(EXE_SUCCESS!= ret){
			PRINT("Error: Failed ECC1, ret=0x%X\r\n", ret);
			return APP_ECC_E_ECC_NOT_DONE;
		}else{
			PRINT("Result: ECC1 Passed.\r\n");
		}
	}
	
	// MAC password
	Get_Reset_Password(reset_password);
	do_mac( mac_result, reset_password, gf2nReturnZ, gf2nCheck, 0, ECC_CURVE_163, DEFAULT_MAC_GROUP);

	// Send MACCR1/MACCR2/MACCR3/MACCR4
	MacData2Byte(mac_result, mac_byte);
	if(lsc_number == 0){
		if(auths_capability.active_interface==I2C){
			ret = i2c_bus_command_MACCR1(auths_capability.interface.i2c.device_address, mac_byte);
			if (INF_I2C_SUCCESS != ret) {
				return APP_LSC_E_MACCR1;
			}
		}
	}
	else if(lsc_number == 1){
		if(auths_capability.active_interface==I2C){
			ret = i2c_bus_command_MACCR2(auths_capability.interface.i2c.device_address, mac_byte);
			if (INF_I2C_SUCCESS != ret) {
				return APP_LSC_E_MACCR2;
			}
		}
	}
	else if(lsc_number == 2){
		if(auths_capability.active_interface==I2C){
			ret = i2c_bus_command_MACCR3(auths_capability.interface.i2c.device_address, mac_byte);
			if (INF_I2C_SUCCESS != ret) {
				return APP_LSC_E_MACCR3;
			}
		}
	}
	else if(lsc_number == 3){
		if(auths_capability.active_interface==I2C){
			ret = i2c_bus_command_MACCR4(auths_capability.interface.i2c.device_address, mac_byte);
			if (INF_I2C_SUCCESS != ret) {
			return APP_LSC_E_MACCR4;
			}
		}
	} else{
		return APP_LSC_E_INPUT;//invalid LSC 
	}
	
	return APP_LSC_SUCCESS;
}

/**
* @brief Get the LSC status. Note that once LSC_1_EN and LSC_2_EN are set(locked), 
* the LSC 1 and 2 Buffer cannot be read/write.
* @param lsc_number to select which life span counter to reset
*/
uint16_t auths_get_lsc_lock_status(uint8_t lsc_number)
{
	uint16_t ret;
	uint8_t rd_data;
	uint8_t bit_lsc_en_sts;

	if(lsc_number == 0){
		bit_lsc_en_sts = BIT_LSC_1_EN_STS;
	}
	else if(lsc_number == 1){
		bit_lsc_en_sts = BIT_LSC_2_EN_STS;
	}
	else if(lsc_number == 2){
		bit_lsc_en_sts = BIT_LSC_3_EN_STS;
	}
	else if(lsc_number == 3){
		bit_lsc_en_sts = BIT_LSC_4_EN_STS;
	}
    else{        
        return APP_LSC_E_INPUT;
    }

	ret = Intf_ReadRegister(AUTHS_SFR_LSC_KILL_FEAT_STS_1, &rd_data, 1);
	if(SDK_INTERFACE_SUCCESS != ret){
		return ret;
	}else{
		if(((rd_data >> bit_lsc_en_sts) & 0x01) == 0){			
			return APP_LSC_NOT_LOCKED;
		} else{
			return APP_LSC_LOCKED;
		}
	}
}

/**
* @brief Get the various IFX configuration. 
* @param config_type IFX configuration type
*/
uint16_t auths_get_ifx_config(CONFIG_TYPE config_type)
{
	uint16_t ret;
	uint8_t rd_data[1];

	ret = Intf_ReadRegister(AUTHS_SFR_LOCK_STS, rd_data, 1);
	if(SDK_INTERFACE_SUCCESS!= ret){
		return ret;
	}

	switch(config_type){
		case NVM_RESET_CONFIG:{
			if(((rd_data[0] >> BIT_IFX_USERNVM_RESET_CONFIG ) & 0x01) == 0){
				return APP_NVM_DISABLE_RESET;
			}else{
				return APP_NVM_ENABLE_RESET;
			}
		}
		case HS_MODE_CONFIG:{
			if(((rd_data[0] >> BIT_HOST_SUP_MODE ) & 0x01) == 0){
				return APP_HOSTSUPP_DISABLE;
			}else{
				return APP_HOSTSUPP_ENABLE;
			}
		}
		case CHIPLOCK_CONFIG:{
			if(((rd_data[0] >> BIT_CHIP_LOCK ) & 0x01) == 0){
				return APP_NVM_DISABLE_CHIPLOCK;
			}else{
				return APP_NVM_ENABLE_CHIPLOCK;
			}
		}
		case LSC_RESET_CONFIG:{
			if(((rd_data[0] >> BIT_LSC_RESET_CONF ) & 0x01) == 0){
				return APP_LSC_DISABLE_RESET;
			}else{
				return APP_LSC_ENABLE_RESET;
			}
		}
		case HOSTAUTH_CONFIG:{
			if(((rd_data[0] >> BIT_HOSTAUTH_CONF ) & 0x01) == 0){
				return APP_HA_DISABLE;
			}else{
				return APP_HA_ENABLE;
			}
		}
		case AUTOKILL_CONFIG:{
			if(((rd_data[0] >> BIT_IFX_AUTO_KILL_CONF ) & 0x01) == 0){
				return APP_ECC_DISABLE_AUTOKILL;
			}else{
				return APP_ECC_ENABLE_AUTOKILL;
			}
		}
		case KILL_CONFIG:{
			if(((rd_data[0] >> BIT_IFX_KILL_CONF ) & 0x01) == 0){
				return APP_ECC_DISABLE_KILL;
			}else{
				return APP_ECC_ENABLE_KILL;
			}
		}
		default:
			return SDK_E_CONFIG;
	}
}
